// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: jasonyoon

#include "coin2/strategy/linear_model/focus_midfreq.h"

namespace coin2::strategy::linear_model {

BlockInfo LmSubStrategy2::BlockOrder(
  int64_t timestamp,
  OrderExecutorSystem* oe,
  AggregatedSizer2* asizer,
  double* adjust_info,
  const bool clear,
  const double current_stack,
  const LmFocusMidfreqConfig& fconf) {
  const auto& pi = pholder->product_info();
  double contract_value = pi.contract_value() > 0? pi.contract_value(): 1.0;

  // mark price
  double midp = (bbo->askp + bbo->bidp) / 2;
  adjust_info[0] = midp;

  double min_quote =
      fconf.min_pos_quote() *
      fconf.max_concentration();
  double max_quote =
      fconf.max_pos_quote() *
      fconf.max_concentration();
  const double init_min_pos = GetQtyFromQuote(*pholder, bbo->askp, min_quote);
  const double init_max_pos = GetQtyFromQuote(*pholder, bbo->askp, max_quote);

  if (use_liq_const_) {
    liquidity_->UpdateTimeOnly(timestamp);
    double max_absquote = contract_value * liquidity_->sum() * fconf.liquidity_constraint();
    min_quote = std::max(min_quote, -max_absquote);
    max_quote = std::min(max_quote, max_absquote);
  }

  double curr_pos = oe->GetCurrentPosition(*pholder);
  double curr_pos_quote = GetQuoteFromPq(
      *pholder,
      bbo->askp,
      curr_pos);
  asizer->UpdatePosition(symbol, curr_pos_quote);
  auto bs = asizer->GetRemainingBuySellQuote();
  double aggr_norm_pos = asizer->GetAggregatedNormalizedPos();
  double aggr_skew_bps = -aggr_norm_pos * fconf.aggr_lean_bps();
  if (fconf.abs_signal_aggr_lean_bps() > 0) {
    double signal_aggr_lean_bps = signal_aggr_moving_mean * fconf.abs_signal_aggr_lean_bps();
    aggr_skew_bps = aggr_skew_bps - aggr_norm_pos * signal_aggr_lean_bps;
  }

  double max_pos = GetQtyFromQuote(*pholder, bbo->askp, max_quote);
  double min_pos = GetQtyFromQuote(*pholder, bbo->askp, min_quote);
  double max_tot_pos = GetQtyFromQuote(*pholder, bbo->askp, fconf.max_pos_quote());
  double min_tot_pos = GetQtyFromQuote(*pholder, bbo->askp, fconf.min_pos_quote());
  double lot_size = (max_pos - min_pos) / fconf.stack();
  double min_order_qty = GetQtyFromQuote(*pholder, bbo->askp, fconf.min_order_quote());

  // hedge variables
  double hedge_signal = 0;
  bool is_hedge_symbol = false;
  double hedge_max_pos = 0;
  double hedge_min_pos = 0;

  if (asizer->IsHedgeSymbol(symbol)) {
    is_hedge_symbol = true;
    const double lot_pq = (max_quote - min_quote) / fconf.stack() * fconf.hedge_lot_mult();
    lot_size = lot_size * fconf.hedge_lot_mult();
    double pos_diff_threshold = fconf.hedge_pos_diff_threshold();
    // get hedge_signal if symbol is in hedge_symbol
    hedge_signal = asizer->GetHedgeSignal(symbol, fconf.max_abspos_quote(), pos_diff_threshold);

    double hedge_lot_pq = 0;
    if ((hedge_signal >= 2) || (hedge_signal <= -2)) {
      // exposure is big, so increase lot_size
      hedge_lot_pq = fconf.max_abspos_quote() * pos_diff_threshold * 1.0;
    } else if ((hedge_signal >= 1) || (hedge_signal <= -1)) {
      // exposure is not that big, so no hurry
      hedge_lot_pq = fconf.max_abspos_quote() * pos_diff_threshold * 0.25;
    } else {
      // this need to be 0, but it can kill executor on some weired circumstance, so give small_value
      hedge_lot_pq = 10;
    }
    lot_size = lot_size * (hedge_lot_pq / lot_pq);

    // need to consider working order of hedge_symbol
    double pos_diff_quote = hedge_signal * fconf.max_abspos_quote() * pos_diff_threshold;
    hedge_max_pos = curr_pos + std::max(0.0, pos_diff_quote);
    hedge_min_pos = curr_pos + std::min(0.0, pos_diff_quote);
  }

  // mark inital max / min pos and lot size
  adjust_info[1] = max_pos; adjust_info[2] = min_pos, adjust_info[3] = lot_size;
  if (is_hedge_symbol) {
    lholder_.ManageExecutorConfig(
        lot_size,
        min_tot_pos / asizer->GetHedgeSymbolCnt(),
        max_tot_pos / asizer->GetHedgeSymbolCnt(),
        timestamp);
  } else {
    lholder_.ManageExecutorConfig(lot_size, init_min_pos, init_max_pos, timestamp);
  }

  // apply fill cool time
  bool buy_block = false, sell_block = false;
  if (use_fill_cooldown_) {
    buy_filled_.UpdateTimeOnly(timestamp);
    sell_filled_.UpdateTimeOnly(timestamp);
    buy_block = buy_filled_.sum() > lot_size * fconf.fill_cooldown_constraint();
    sell_block = sell_filled_.sum() > lot_size * fconf.fill_cooldown_constraint();
    adjust_info[13] = static_cast<double>(buy_block);
    adjust_info[14] = static_cast<double>(sell_block);
  }

  // apply pr constraint
  if (fconf.pr_constraint() > 0) {
    buy_turnover_.UpdateTimeOnly(timestamp);
    sell_turnover_.UpdateTimeOnly(timestamp);
    market_turnover_->UpdateTimeOnly(timestamp);
    double max_pr = fconf.pr_constraint();
    double market_turnover = market_turnover_->sum();
    double turnover = buy_turnover_.sum() + sell_turnover_.sum();
    bool pr_exceed = market_turnover * max_pr < turnover;
    adjust_info[11] = market_turnover;
    adjust_info[12] = turnover;
    buy_block = buy_block || pr_exceed;
    sell_block = sell_block || pr_exceed;
  }

  // apply exposure constraint
  if (fconf.exposure_constraint() > 0) {
    double net_exposure = asizer->GetAggregatedPosQuote();
    if (curr_pos > 0 &&
        net_exposure > fconf.max_pos_quote() * fconf.exposure_constraint())
      buy_block = true;
    if (curr_pos < 0 &&
        net_exposure < fconf.min_pos_quote() * fconf.exposure_constraint())
      sell_block = true;
  }

  adjust_info[15] = stack_queue_->GetFirstTimestampDiff(timestamp);

  // apply book constraint
  double remain_buy_qty = lot_size, remain_sell_qty = lot_size;
  if (fconf.book_constraint() > 0) {
    double ratio = fconf.book_constraint();

    ask_bbo_->UpdateTimeOnly(timestamp);
    bid_bbo_->UpdateTimeOnly(timestamp);
    double ask_bbo = ask_bbo_->average();
    double bid_bbo = bid_bbo_->average();
    auto& tickprc = pholder->product_info().tick_price();
    double book_mult = tickprc.Infer2bpBookMult(bbo->askp);

    remain_buy_qty = std::max(0.0, bid_bbo * book_mult * ratio / bbo->askp);
    remain_sell_qty = std::max(0.0, ask_bbo * book_mult * ratio / bbo->askp);
    max_pos = std::min(max_pos, curr_pos + remain_buy_qty);
    min_pos = std::max(min_pos, curr_pos - remain_sell_qty);
  }

  // mark adjusted qty by book constraint
  adjust_info[4] = remain_buy_qty; adjust_info[5] = remain_sell_qty;

  // backward compatibility
  bool _clear = fconf.exec_type() == MidfreqExecType::CLEAR_EXEC ||
      fconf.clear_mode() || clear;

  if (!_clear) {
    int hedge_symbol_cnt = asizer->GetHedgeSymbolCnt();
    // if not_hedge_symbol, block on pos_constraint
    if (!asizer->IsHedgeSymbol(symbol)) {
      // limit exposure
      if (fconf.use_aggregated_pos()) {
        buy_block = buy_block || bs.first <= 0;
        sell_block = sell_block || bs.second <= 0;
      }

      // limit abspos quote
      if (fconf.max_abspos_quote() > 0) {
        auto bs2 = asizer->GetRemainingAbsBuySellQuote(symbol);
        buy_block = buy_block || bs2.first <= 0;
        sell_block = sell_block || bs2.second <= 0;
      }
    } else { // case of hedge symbol, no_block
      max_pos = std::min(hedge_max_pos, max_tot_pos / hedge_symbol_cnt);
      min_pos = std::max(hedge_min_pos, min_tot_pos / hedge_symbol_cnt);
    }

    if (asizer->symbols_clear.count(symbol) > 0) {
      max_pos = 0;
      min_pos = 0;
    }

    oe->AdjustSize(*pholder, min_pos, max_pos,
        lot_size, min_order_qty);

    if (fconf.longonly())
      sell_block |= current_stack <= 0;
    adjust_info[6] = current_stack;
    adjust_info[7] = aggr_norm_pos;
  }

  // return BlockInfo
  BlockInfo ret;
  ret.max_pos = max_pos; ret.min_pos = min_pos; ret.lot_size = lot_size;
  ret.buy_block = buy_block; ret.sell_block = sell_block;
  ret.aggr_skew_bps = aggr_skew_bps; ret.min_order_qty = min_order_qty;
  ret.remain_buy_qty = remain_buy_qty; ret.remain_sell_qty = remain_sell_qty;
  ret.curr_pos = curr_pos;
  ret.hedge_signal = hedge_signal; ret.is_hedge_symbol = is_hedge_symbol;
  return ret;
}

int LmSubStrategy2::ClearPosition(
    int64_t timestamp,
    OrderExecutorSystem* oe,
    BlockInfo* block_ptr,
    const double leverage_rate) {
  int ret = 0;
  auto& block = *block_ptr;
  auto& tickprc = pholder->product_info().tick_price();
  ret += oe->CancelAllOrders(*pholder, OrderDirection::DIRECTION_SELL, timestamp);
  ret += oe->CancelAllOrders(*pholder, OrderDirection::DIRECTION_BUY, timestamp);
  if (block.curr_pos > 0 && !block.sell_block) {
    block.lot_size = std::min(block.lot_size, block.remain_sell_qty);
    oe->AdjustSize(*pholder, 0, 0, block.lot_size);
    auto oe_res = oe->SubmitOrder(
        *pholder,
        tickprc.RoundPassively(bbo->askp, OrderDirection::DIRECTION_SELL),
        block.lot_size,
        OrderDirection::DIRECTION_SELL,
        OrderDuration::GTC_ORDER,
        OrderType::LIMIT_ORDER,
        bbo->askp,
        leverage_rate,
        true,  // post_only
        "maker",
        timestamp,
        false,
        false,
        std::nullopt,  // reduce only
        true);
    if (oe_res.qty > 0) ret++;
  } else if (block.curr_pos < 0 && !block.buy_block) {
    block.lot_size = std::min(block.lot_size, block.remain_buy_qty);
    oe->AdjustSize(*pholder, 0, 0, block.lot_size);
    auto oe_res = oe->SubmitOrder(
        *pholder,
        tickprc.RoundPassively(bbo->bidp, OrderDirection::DIRECTION_BUY),
        block.lot_size,
        OrderDirection::DIRECTION_BUY,
        OrderDuration::GTC_ORDER,
        OrderType::LIMIT_ORDER,
        bbo->bidp,
        leverage_rate,
        true,  // post_only
        "maker",
        timestamp,
        false,
        false,
        std::nullopt,  // reduce only
        true);
    if (oe_res.qty > 0) ret++;
  }
  return ret;
}

int LmSubStrategy2::BboExecution(
    OrderExecutorSystem* oe,
    double* adjust_info,
    const int64_t timestamp,
    const double midret_pred,
    const double sigma,
    const double current_stack,
    const double leverage_rate,
    const BlockInfo& block,
    const LmFocusMidfreqConfig& fconf) {

  double signal_symbol_lean_bps = 0;
  if (fconf.abs_signal_symbol_lean_bps() > 0) {
    signal_symbol_lean_bps = fconf.abs_signal_symbol_lean_bps() * signal_->average();
  }

  double signal_lb = midret_pred - fconf.nsigma_edge() * sigma;
  double signal_ub = midret_pred + fconf.nsigma_edge() * sigma;

  double single_pos_lean_mul = std::pow(std::abs(current_stack) / fconf.stack() * 2.0, fconf.stack_p());
  single_pos_lean_mul = current_stack > 0? -single_pos_lean_mul: single_pos_lean_mul;

  double single_pos_lean_buy = fconf.single_pos_lean_bps();
  double single_pos_lean_sell = fconf.single_pos_lean_bps();
  if (fconf.single_pos_clear_lean_bps() > 0) {
    if (current_stack > 0) {
      single_pos_lean_sell = fconf.single_pos_clear_lean_bps();
    } else {
      single_pos_lean_buy = fconf.single_pos_clear_lean_bps();
    }
  }

  double skew_buy_bps = single_pos_lean_mul * (single_pos_lean_buy + signal_symbol_lean_bps) + block.aggr_skew_bps;
  double skew_sell_bps = single_pos_lean_mul * (single_pos_lean_sell + signal_symbol_lean_bps) + block.aggr_skew_bps;
  if (fconf.max_lean_bps() > 0) {
    skew_buy_bps = std::clamp(skew_buy_bps, -fconf.max_lean_bps(), fconf.max_lean_bps());
    skew_sell_bps = std::clamp(skew_sell_bps, -fconf.max_lean_bps(), fconf.max_lean_bps());
  }
  // double signal_skewed_lb = signal_lb + skew_bps;
  double signal_skewed_lb = signal_lb + skew_buy_bps;
  double signal_skewed_ub = signal_ub + skew_sell_bps;

  if (fconf.holding_lean_bps() > 0 and std::abs(current_stack) > 0.5) {
    double holding_lean_bps = stack_queue_->GetHoldingLean(timestamp);
    double threshold_width = fconf.buy_threshold_bps() - fconf.sell_threshold_bps();
    holding_lean_bps = std::clamp(holding_lean_bps, -threshold_width, threshold_width);
    if (current_stack > 0) {
      signal_skewed_ub -= holding_lean_bps;
    } else {
      signal_skewed_lb += holding_lean_bps;
    }
  }

  // if B, E hedge needed, make signal 1000 to force buy & sell
  if (block.hedge_signal > 1) {
    signal_skewed_lb = 1000;
    signal_skewed_ub = 1000;
    DLOG(INFO) << fmt::format("time : {}, be_signal : {}, buy_block: {}, sell_block: {}, lot_size: {}",
        timestamp, block.hedge_signal, block.buy_block, block.sell_block, block.lot_size);
  } else if (block.hedge_signal < -1) {
    signal_skewed_lb = -1000;
    signal_skewed_ub = -1000;
    DLOG(INFO) << fmt::format("time : {}, be_signal : {}, buy_block: {}, sell_block: {}, lot_size: {}",
    timestamp, block.hedge_signal, block.buy_block, block.sell_block, block.lot_size);
  } else if (block.is_hedge_symbol) { // set_signal = 0 if hedge_symbol
    signal_skewed_lb = 0;
    signal_skewed_ub = 0;
    DLOG(INFO) << fmt::format("time : {}, be_signal : {}, buy_block: {}, sell_block: {}, lot_size: {}",
    timestamp, block.hedge_signal, block.buy_block, block.sell_block, block.lot_size);
  }
  // some hard coded variables yet
  int ret = ManageMmOrder2(
      oe,
      *pholder,
      *bbo,
      timestamp,
      &bom,
      leverage_rate,

      signal_skewed_lb,
      signal_skewed_ub,
      GetVolatility(),

      block.buy_block,
      block.sell_block,

      // edge bps
      fconf);
  adjust_info[9] = signal_skewed_lb;
  adjust_info[10] = signal_skewed_ub;
  return ret;
}

int LmSubStrategy2::PortfolioExecution(
    OrderExecutorSystem* oe,
    double* adjust_info,
    const int64_t timestamp,
    const double midret_pred,
    const double* feats,
    const double leverage_rate,
    BlockInfo* block_ptr,
    const LmFocusMidfreqConfig& fconf) {
  // position based execution

  auto& block = *block_ptr;

  midp_avg_->UpdateTimeOnly(timestamp);
  double midp = midp_avg_->average();
  double interval_pos_quote = feats[TARGET_POSITION];
  double time_remaining = feats[TIME_REMAINING];
  bool new_interval = false;

  /*****
    Calculate target qty on interval
  ******/

  if (time_remaining > last_time_remaining_) {
    last_interval_pos_ = interval_pos_;
    interval_pos_ = GetQtyFromQuote(*pholder, midp, interval_pos_quote);

    new_interval = true;
  }
  last_time_remaining_ = time_remaining;

  double interval_split_span = 1.0 / static_cast<double>(n_interval_split_);
  int32_t interval_split_index = std::max(static_cast<int32_t>(time_remaining / interval_split_span), 0);
  if (new_interval || interval_split_index != last_interval_split_index_) {
    last_interval_split_index_ = interval_split_index;

    last_target_pos_ = last_interval_pos_ +
      ((interval_pos_ - last_interval_pos_) * (1 - (interval_split_span * interval_split_index)));
  }

  double target_pos = last_target_pos_;
  double diff = target_pos - block.curr_pos;

  // save tps when position is almost target position
  if (std::abs(diff) < 0.01 * block.lot_size)
    return 0;

  double signal = 0;

  //if (pholder->product().relative_norm() == "BTC-USDT.PERPETUAL")
  //  LOG(INFO) << fmt::format("symbol: {}, time remain: {}, time index: {}, target itv pq: {}, midp: {}, last target pos: {}, curr pos: {}, diff: {}",
  //      pholder->product().relative_norm(), time_remaining, interval_split_index, interval_pos_quote, midp, last_target_pos_, block.curr_pos, diff);

  double time_lean_bps = std::abs(diff) / block.lot_size / feats[TIME_REMAINING];
  if (diff > 0) {
    // buy action
    block.min_pos = std::max(block.curr_pos, block.min_pos);
    block.max_pos = std::min(target_pos, block.max_pos);
    block.sell_block = true;
    signal = midret_pred + time_lean_bps;
    // signal = fconf.buy_threshold_bps() + 1;
  } else {
    // sell action
    block.min_pos = std::max(target_pos, block.min_pos);
    block.max_pos = std::min(block.curr_pos, block.max_pos);
    block.buy_block = true;
    signal = midret_pred - time_lean_bps;
    // signal = fconf.sell_threshold_bps() - 1;
  }

  // adjust order size
  oe->AdjustSize(*pholder, block.min_pos, block.max_pos,
      block.lot_size, block.min_order_qty);


  // some hard coded variables yet
  int ret = ManageMmOrder2(
      oe,
      *pholder,
      *bbo,
      timestamp,
      &bom,
      leverage_rate,

      signal,
      signal,
      GetVolatility(),

      block.buy_block,
      block.sell_block,

      // edge bps
      fconf);
  adjust_info[9] = target_pos / block.lot_size;
  adjust_info[10] = signal;
  return ret;
}

int LmSubStrategy2::MmExecution(
    OrderExecutorSystem* oe,
    double* adjust_info,
    const int64_t timestamp,
    const double midret_pred,
    double sigma,
    const double current_stack,
    const double leverage_rate,
    const BlockInfo& block,
    const LmFocusMidfreqConfig& fconf,
    const double* feats) {
  int ret = 0;
  if (bbo->askp <= 0 || bbo->bidp <= 0) return ret;

  // get basic signal lb / ub from midret_pred
  double buy_bps = midret_pred, sell_bps = midret_pred;
  const double buy_threshold_bps = fconf.buy_threshold_bps();
  const double sell_threshold_bps = fconf.sell_threshold_bps();

  // apply mm spread
  buy_bps -= fconf.mm_spread_bps();
  sell_bps += fconf.mm_spread_bps();
  double signal_symbol_lean_bps = 0;
  if (fconf.abs_signal_symbol_lean_bps() > 0) {
    signal_symbol_lean_bps = fconf.abs_signal_symbol_lean_bps() * signal_->average();
  }

  // TODO(soony) : udpate adaptive sigma
  // sigma += std::max((GetVolatility() - fconf.volatility_const()) * fconf.volat_const(), 0.0);

  // apply single pos leaning
  double single_pos_lean_mul = std::pow(std::abs(current_stack) / fconf.stack() * 2.0, fconf.stack_p());
  single_pos_lean_mul = current_stack > 0? -single_pos_lean_mul: single_pos_lean_mul;
  double single_pos_lean_buy = current_stack > 0?
    fconf.single_pos_lean_bps2(): fconf.single_pos_lean_bps1();
  double single_pos_lean_sell = current_stack > 0?
    fconf.single_pos_lean_bps1(): fconf.single_pos_lean_bps2();
  buy_bps += (single_pos_lean_mul * (single_pos_lean_buy + signal_symbol_lean_bps) + block.aggr_skew_bps);
  sell_bps += (single_pos_lean_mul * (single_pos_lean_sell + signal_symbol_lean_bps) + block.aggr_skew_bps);

  if (timestamp - last_print_ts_ > 1'000'000'000LL && fconf.logging_signal()) {
    double book0_midp = (bbo->askp + bbo->bidp) * 0.5;
    double book0_spread = (bbo->askp - bbo->bidp) / book0_midp;
    double book1_midp = (feats[REF_ASKP] + feats[REF_BIDP]) * 0.5;
    double book1_spread = (feats[REF_ASKP] - feats[REF_BIDP]) / book1_midp;
    LOG(INFO) << fmt::format(
        "buy bps: {}, sell bps: {}, pos lean buy: {}, pos lean sell: {}, "
        "book0 spread: {}, book1 spread: {}",
        buy_bps, sell_bps,
        single_pos_lean_mul * single_pos_lean_buy,
        single_pos_lean_mul * single_pos_lean_sell,
        book0_spread * 1e4, book1_spread * 1e4);
    last_print_ts_ = timestamp;
  }

  // if B, E hedge needed, make signal 1000 to force buy & sell
  if (block.hedge_signal > 1) {
    buy_bps = 1000;
    sell_bps = 1000;
    DLOG(INFO) << fmt::format("be signal : {}, buy_block: {}, sell_block: {}",
        block.hedge_signal, block.buy_block, block.sell_block);
  } else if (block.hedge_signal < -1) {
    buy_bps = -1000;
    sell_bps = -1000;
    DLOG(INFO) << fmt::format("be signal : {}, buy_block: {}, sell_block: {}",
    block.hedge_signal, block.buy_block, block.sell_block);
  } else if (block.is_hedge_symbol) { // set signal=0 if hedge_symbol
    buy_bps = 0;
    sell_bps = 0;
    DLOG(INFO) << fmt::format("time : {}, be_signal : {}, buy_block: {}, sell_block: {}, lot_size: {}",
    timestamp, block.hedge_signal, block.buy_block, block.sell_block, block.lot_size);
  }



  ret += ManageMmOrder3(
      oe,
      *pholder,
      *bbo,
      timestamp,
      &bom,
      leverage_rate,
      buy_bps,
      sell_bps,
      sigma,
      buy_threshold_bps,
      sell_threshold_bps,
      block.buy_block,
      block.sell_block,
      fconf);
  adjust_info[9] = buy_bps;
  adjust_info[10] = sell_bps;

  return ret;
}

int LmSubStrategy2::LmExecution(
    OrderExecutorSystem* oe,
    double* adjust_info,
    const int64_t timestamp,
    const double midret_pred,
    const double sigma,
    const double current_stack,
    const double leverage_rate,
    const BlockInfo& block,
    const LmFocusMidfreqConfig& fconf) {
  double signal_lb = midret_pred - fconf.nsigma_edge() * sigma;
  double signal_ub = midret_pred + fconf.nsigma_edge() * sigma;
  double skew_bps = -current_stack / fconf.stack() * 2 * fconf.single_pos_lean_bps()
    + block.aggr_skew_bps;
  if (fconf.max_lean_bps() > 0)
    skew_bps = std::clamp(skew_bps, -fconf.max_lean_bps(), fconf.max_lean_bps());
  double signal_skewed_lb = signal_lb + skew_bps;
  double signal_skewed_ub = signal_ub + skew_bps;

  DLOG(INFO) << fmt::format(
    "GetVol: {}, GetSpread: {}, GetBP: {}, "
    "vol_con: {}, spread_con: {}, bp_con: {}",
    GetVolatility(),
    GetSpread(),
    GetBookPressure(),
    fconf.volatility_const(),
    fconf.spread_const(),
    fconf.book_pressure_const());

  // some hard coded variables yet
  int ret = ManageLmOrder(
      oe,
      *pholder,
      *bbo,
      timestamp,

      &prev_ask_post_time,
      &prev_bid_post_time,
      &prev_ask_fill_time,
      &prev_bid_fill_time,
      leverage_rate,

      signal_skewed_lb,
      signal_skewed_ub,
      GetVolatility(),
      GetSpread(),
      GetBookPressure(),
      block.buy_block,
      block.sell_block,

      // edge bps
      fconf);
  adjust_info[9] = signal_skewed_lb;
  adjust_info[10] = signal_skewed_ub;
  return ret;
}

// this is for bbo_execution + time_window_liquidation
int LmSubStrategy2::TargetPosBboExecution(
    OrderExecutorSystem* oe,
    double* adjust_info,
    const int64_t timestamp,
    const double midret_pred,
    const double sigma,
    const double current_stack,
    const double leverage_rate,
    const BlockInfo& block,
    const LmFocusMidfreqConfig& fconf) {
  double signal_lb = midret_pred - fconf.nsigma_edge() * sigma;
  double signal_ub = midret_pred + fconf.nsigma_edge() * sigma;
  double skew_bps = -current_stack / fconf.stack() * 2 * fconf.single_pos_lean_bps()
    + block.aggr_skew_bps;
  if (fconf.max_lean_bps() > 0)
    skew_bps = std::clamp(skew_bps, -fconf.max_lean_bps(), fconf.max_lean_bps());
  double signal_skewed_lb = signal_lb + skew_bps;
  double signal_skewed_ub = signal_ub + skew_bps;

  // if B, E hedge needed, make signal 1000 to force buy & sell
  if (block.hedge_signal == 1) {
    signal_skewed_lb = 1000;
    signal_skewed_ub = 1000;
    DLOG(INFO) << fmt::format("be signal : {}, buy_block: {}, sell_block: {}",
        block.hedge_signal, block.buy_block, block.sell_block);
  } else if (block.hedge_signal == -1) {
    signal_skewed_lb = -1000;
    signal_skewed_ub = -1000;
    DLOG(INFO) << fmt::format("be signal : {}, buy_block: {}, sell_block: {}",
    block.hedge_signal, block.buy_block, block.sell_block);
  }

  DLOG(INFO) << fmt::format("pos_wom symbol : {}, get_target_pos : {}",
      pholder->product().relative_norm(), pos_wom_->get_target_position());

  // some hard coded variables yet
  int ret = ManageMmOrder5(
      oe,
      *pholder,
      *bbo,
      timestamp,
      &bom,
      leverage_rate,

      signal_skewed_lb,
      signal_skewed_ub,
      GetVolatility(),

      block.buy_block,
      block.sell_block,

      // edge bps
      fconf,
      block.curr_pos,
      pos_wom_,
      calc_target_pos_);
  adjust_info[9] = signal_skewed_lb;
  adjust_info[10] = signal_skewed_ub;
  return ret;
}



}  // namespace coin2::strategy::linear_model
